/**
 * ZHU framework. Copyright 2012-, all rights reserved.
 *
 * $Id: ToStringBuilder.java 33 2012-08-30 08:10:18Z zhuxiaopeng $
 * $Revision: 33 $
 * $Date: 2012-08-30 16:10:18 +0800 (四, 2012-08-30) $
 * $Author: zhuxiaopeng $
 */
package zhu.framework.util;

import java.util.*;
import java.util.logging.*;

import javax.inject.*;

/**
 * <p>
 * </p>
 * 
 * @author $Author: zhuxiaopeng $
 * @version $Revision: 33 $ - $Date: 2012-08-30 16:10:18 +0800 (四, 2012-08-30) $
 */
public final class ToStringBuilder {

	public ToStringBuilder(final Class<?> clazz) {
		this(clazz, true);
	}

	public ToStringBuilder(final Class<?> clazz, final boolean fullyQualified) {
		this(clazz, fullyQualified, 512);
	}

	public ToStringBuilder(final Class<?> clazz, final boolean fullyQualified, final int length) {
		MyUtility.checkMethodParameterNotNull("clazz", clazz);
		//
		buffer = new StringBuilder(length);
		if (fullyQualified) {
			buffer.append(clazz.getName());
		} else {
			buffer.append(getNonQualifiedName(clazz));
		}
		buffer.append("[");
	}

	private String getNonQualifiedName(final Class<?> clazz) {
		final String fqn = clazz.getName();
		final int lastPeriod = fqn.lastIndexOf('.');
		return fqn.substring(lastPeriod + 1);
	}

	public ToStringBuilder append(final String fieldName, final Object fieldValue) {
		MyUtility.checkMethodParameterNotNull("fieldName", fieldName);
		checkFinished();
		appendSeparatorIfNecessary();
		buffer.append(fieldName);
		buffer.append("=");
		buffer.append(fieldValue);
		setFieldAdded();
		return this;
	}

	public ToStringBuilder append(final String fieldName, final boolean fieldValue) {
		return this.append(fieldName, Boolean.valueOf(fieldValue));
	}

	public ToStringBuilder append(final String fieldName, final long fieldValue) {
		return this.append(fieldName, Long.valueOf(fieldValue));
	}

	public ToStringBuilder append(final String fieldName, final int fieldValue) {
		return this.append(fieldName, Integer.valueOf(fieldValue));
	}

	public ToStringBuilder append(final String fieldName, final Collection<?> fieldValue) {
		MyUtility.checkMethodParameterNotNull("fieldName", fieldName);
		final StringBuilder values = new StringBuilder(1024);
		values.append(fieldValue.getClass().getName());
		values.append("{");
		boolean firstElement = true;
		final Iterator<?> it = fieldValue.iterator();
		while (it.hasNext()) {
			if (firstElement) {
				firstElement = false;
			} else {
				values.append(", ");
			}
			values.append(it.next());
		}
		values.append("}");
		return this.append(fieldName, values.toString());
	}

	/**
	 * <p>
	 * フィールド名は<code>toString()</code>の出力に含めたいが、フィールド値にアクセスしたくない場合（Lazy
	 * LoadされるEntityのフィールド等）に使用する。 出力は、<code>fieldName=&lt;skip-lazy&gt;</code>
	 * となる。
	 * </p>
	 * 
	 * @param fieldName
	 *            フィールド名
	 * @return このオブジェクト
	 */
	public ToStringBuilder appendLazy(final String fieldName) {
		return this.append(fieldName, "<skip-lazy>");
	}

	/**
	 * <p>
	 * このオブジェクトが構築している<code>toString()</code>文字列を取得する。
	 * このメソッドの呼び出し以降、フィールド情報をappendする操作は全て
	 * <code>{@link IllegalStateException}</code>を送出する。
	 * </p>
	 * 
	 * @return 構築済みの<code>toString()</code>文字列
	 */
	@Override
	public String toString() {
		buffer.append("]");
		finished = true;
		return buffer.toString();
	}

	private void checkFinished() {
		if (finished) {
			final IllegalStateException e = new IllegalStateException("ToStringBuilder is already made unmodifiable by calling toString()");
			logger.throwing("educom.framework.util.ToStringBuilder", "checkFinished", e);
			throw e;
		}
	}

	private boolean isFirstField() {
		return firstField;
	}

	private void appendSeparatorIfNecessary() {
		if (isFirstField() == false) {
			buffer.append("; ");
		}
	}

	private void setFieldAdded() {
		firstField = false;
	}

	private boolean firstField = true;
	private boolean finished = false;
	private final StringBuilder buffer;

	@Inject
	private Logger logger;
}
